Skip to content

SSLConfig: fix stale allocator names in dupe_z doc comments - #31032

Closed
robobun wants to merge 1 commit into
mainfrom
farm/5601c27f/security-scanner-over-deref
Closed

robobun wants to merge 1 commit into
mainfrom
farm/5601c27f/security-scanner-over-deref

Conversation

@robobun

@robobun robobun commented May 19, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #30875, requested in #30875 (comment).

Problem

  • Two doc comments in src/runtime/socket/SSLConfig.rs (lines 61 and 67) name the wrong allocator. One says mi_free frees the dupe_z result. The other says dupe_z makes "a fresh mimalloc allocation".
  • Both contradict the block comment above them, which Fix effectively every native-code memory leak in Bun #30875 rewrote. bun_core::dupe_z allocates with bun_alloc::default_alloc::malloc (src/bun_core/util.rs:3685). free_sensitive_cstr frees with default_alloc::free (src/bun_alloc/lib.rs:1038). Under cfg(bun_asan) that is libc, not mimalloc.

Fix

  • Change the two comments to say free_sensitive and "default-allocator allocation". No code changes.
  • No test. The diff is two doc-comment lines, so the compiled binary is identical before and after. There is no behavior a test can observe.
  • Verified: cargo check -p bun_runtime is clean. bun bd builds and links.

Background

  • SSLConfig owns C strings (keys, passphrases) that the HTTP layer frees. The allocation and the free must use the same allocator. The block comment at lines 52 to 59 documents this contract. The two comments below it are the ones this PR corrects.
Notes

This PR first also carried a fix for src/install/PackageManager/security_scanner.rs. finish_spawn released the start() ref on both result arms, but StaticPipeWriter::start() already releases its own ref on Err. On the Err path this freed the writer while json_writer still pointed at it, and the cleanup guard then touched freed memory. #40478 (82123d3a61) fixed this on main: finish_spawn now returns before the deref when start() fails, and the RefPtr locals release on Drop. The rebase onto that commit dropped the security_scanner.rs hunk as superseded. Only the doc comment change remains.

No regression test: the diff changes comments only.

@robobun

robobun commented May 19, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:14 PM PT - Aug 25th, 2026

✅ @robobun, your commit 1f9b8bea7d06241636a0ebffb3353fe5cfb81258 passed in Build #105891! 🎉


🧪   To try this PR locally:

bunx bun-pr 31032

That installs a local version of the PR into your bun-31032 executable, so you can run:

bun-31032 --bun

@coderabbitai

coderabbitai Bot commented May 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR contains two unrelated corrections to runtime and package manager code. The security scanner now guards unsafe refcount operations behind a success check, and the SSL configuration documentation clarifies the allocator contract for a helper function.

Changes

Safety and documentation fixes

Layer / File(s) Summary
SecurityScanSubprocess refcount guard
src/install/PackageManager/security_scanner.rs
In finish_spawn, the intrusive RefCount::<StaticPipeWriter>::deref(writer_ptr) call and started = false reset now occur conditionally only when StaticPipeWriter::start() returns Ok, preventing unsafe state manipulation on error paths.
SSLConfig allocator documentation
src/runtime/socket/SSLConfig.rs
Documentation for the zbox_into_raw helper updated to reflect that bun_core::ZBox uses the global default allocator and must be freed with bun_core::free_sensitive, replacing previous mimalloc-specific wording.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately describes the SSLConfig documentation update, but it does not mention the security scanner fix, which is the primary functional change in the summarized diff.
Description check ✅ Passed The description explains the problem, fix, verification steps, and superseded security scanner change. It does not use the exact template headings, but it provides the required information and is most…
Full details: Description check

Explanation

The description explains the problem, fix, verification steps, and superseded security scanner change. It does not use the exact template headings, but it provides the required information and is mostly complete.


Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented May 19, 2026

Copy link
Copy Markdown
Collaborator Author

Closing per the no-test gate — this one genuinely can't be covered by a regression test:

  • security_scanner.rs: the over-deref only fires when StaticPipeWriter::start() returns Err, which on POSIX reduces to PosixBufferedWriter::start → FilePoll::register_with_fd → epoll_ctl(ADD) / kevent failing on a freshly-created socketpair fd. The only real-world triggers are kernel ENOMEM / ENOSPC (max_user_watches); there's no fault-injection hook in the poll layer to force it, and the Ok path is byte-for-byte identical before and after the change so no happy-path test can discriminate.
  • SSLConfig.rs: doc-comment-only change (stale mi_free / "mimalloc" references → free_sensitive / "default-allocator"), no runtime behavior.

The refcount trace in the PR body is the proof of correctness. Branch farm/5601c27f/security-scanner-over-deref is left pushed; feel free to reopen + merge.

@robobun robobun closed this May 19, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — verified start() self-derefs on Err (static_pipe_writer.rs:196/210), so gating the caller deref on is_ok() is correct and the Ok path is unchanged.

Extended reasoning...

Overview

Follow-up to #30875 addressing two inline review comments. In security_scanner.rs, wraps the post-start() RefCount::deref + started = false write in if start_result.is_ok(). In SSLConfig.rs, updates two stale doc-comment words (mi_free → free_sensitive, mimalloc → default-allocator) to match the already-updated block comment above them.

Verification

I checked src/spawn/static_pipe_writer.rs:175-228: start() does ref_() up front, and on the Err branch (both Windows :196 and POSIX :210) it self-deref()s and never sets started = true. So on Err the function is already refcount-neutral and started is already false — the caller's unconditional deref was indeed an over-deref, and the PR's refcount trace (2 → 3 → 2 → 1 → 0 → UAF/double-free via the scopeguard) is accurate. On Ok, the gated block runs exactly as before, so success-path behavior is byte-identical.

Security risks

None. No new inputs, no parsing, no auth/crypto surface. The change narrows when an unsafe deref runs; it cannot introduce a new unsafe path.

Level of scrutiny

This is intrusive-refcount code in unsafe Rust, which normally warrants care, but the diff is a 4-line conditional wrapper around existing code with no change to the common (Ok) path. Reachability of the fixed path is limited to PosixBufferedWriter::start failing on a fresh pipe fd (epoll/kqueue ENOMEM/ENOSPC), so risk of regression is very low. The SSLConfig changes are comment-only.

Other factors

This directly closes out two review-comment threads from #30875; cargo check and the security-scanner test suite pass per the description; no bugs were flagged by the bug-hunting pass.

@robobun robobun reopened this May 21, 2026
@robobun
robobun force-pushed the farm/5601c27f/security-scanner-over-deref branch from 4daf5f5 to aa018cd Compare May 21, 2026 21:22
@robobun

robobun commented May 21, 2026

Copy link
Copy Markdown
Collaborator Author

Reopened + rebased onto main (aa018cd onto 832edbc) per Discord — re-verified against current main:

  • StaticPipeWriter::start() (static_pipe_writer.rs:183/196/210) is still refcount-neutral on Err
  • security_scanner.rs:1394 on main still derefs unconditionally → over-deref on the Err path (trace in PR body holds)
  • Other callers already gate on started (subprocess.rs:220) or don't extra-deref (shell/subproc.rs:891)
  • SSLConfig.rs: bun_core::dupe_z → default_alloc::malloc (util.rs:4162), so the mi_free/mimalloc comments were stale

No regression test: the Err path needs epoll_ctl(ADD)/kevent to fail on a fresh socketpair fd (kernel ENOMEM/ENOSPC only); no fault-injection hook in FilePoll::register_with_fd, and the Ok path is byte-identical before/after so no happy-path test can discriminate.

cargo check -p bun_install clean on the rebased commit.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — verified StaticPipeWriter::start() self-derefs on Err (static_pipe_writer.rs:196/210), so gating the caller's deref on is_ok() is correct; Ok path is unchanged.

Extended reasoning...

Overview

Two changes, both follow-ups to review comments on #30875:

  1. src/install/PackageManager/security_scanner.rs (finish_spawn): wraps the post-start() intrusive deref + started = false reset in if start_result.is_ok(). I verified against src/spawn/static_pipe_writer.rs:183/196/210 that start() takes a +1 at entry and self-releases it on the Err path (both Windows and POSIX), leaving started == false. So the unconditional caller-side deref was indeed an over-deref on Err, and the refcount trace in the PR body (→ UAF in the scopeguard, double-free) is accurate. The Ok path executes the exact same two statements as before, just inside the if — byte-for-byte identical happy-path behavior.
  2. src/runtime/socket/SSLConfig.rs: two doc-comment words updated (mi_free → free_sensitive, mimalloc → default-allocator) to match the adjacent block comment that already references bun_core::free_sensitive / default_alloc. Zero runtime impact.

Security risks

None. The refcount fix only affects an error path that requires epoll_ctl(ADD)/kevent to fail with ENOMEM/ENOSPC on a freshly-created socketpair fd — not attacker-reachable, and the change strictly removes a UAF/double-free rather than introducing one. The SSLConfig change is comment-only.

Level of scrutiny

Low. The diff is +8/-4 effective lines, the Ok path is provably unchanged, and the Err-path reasoning is mechanically verifiable against the start() source I checked. No CODEOWNERS entries cover either file.

Other factors

  • Bug-hunting system found no issues; CodeRabbit found no actionable comments.
  • cargo check and the security-scanner workspace test pass per the PR body / rebase comment.
  • The lack of a regression test is well-justified in the timeline (no fault-injection hook for the kernel error, and the Ok path can't discriminate before/after).
  • Other StaticPipeWriter::start() callers were already checked by the author (subprocess.rs:220 gates on started, shell/subproc.rs:891 doesn't extra-deref), so this was the only stale call site.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

This over-deref was found again independently while working on the POSIX pipe writers, so I re-checked this PR against current main (b555e06), which includes #37774's rework of how StaticPipeWriter releases its start() ref:

  • StaticPipeWriter::start() still releases its own +1 and leaves started == false when the underlying writer fails to start (src/spawn/static_pipe_writer.rs:189 on POSIX, :175 on Windows).
  • On POSIX that failure is PosixBufferedWriter::start returning the register_with_fd error directly (src/io/PipeWriter.rs:539); no on_error/on_close runs, so the json_writer field still holds its ref at that point.
  • finish_spawn on main still derefs unconditionally after start() (src/install/PackageManager/security_scanner.rs:1323) and again in the Err arm (:1328), so the writer is freed while json_writer still points at it and the cleanup guard at :1308 runs source.detach() + deref() on the freed allocation. The trace in the PR body holds unchanged.
  • Gating on start_result.is_ok() leaves the Ok path as it is today (which is balanced) and makes the Err path end at refcount 0 via the guard, so the fix is still the right one.

#37898 touches the same lines from the Windows side (it gates the scanner's release on the started token instead), so whichever of the two lands second needs a small rebase here.

bun_core::dupe_z allocates with default_alloc::malloc and
free_sensitive_cstr frees with default_alloc::free. Two doc comments
in SSLConfig.rs still said mi_free and mimalloc. Update them to match
the block comment above them.

The security_scanner.rs change this PR first carried is already on
main. #40478 returns early on the start() error before the deref, and
the RefPtr locals release on Drop.
@robobun
robobun force-pushed the farm/5601c27f/security-scanner-over-deref branch from aa018cd to 1f9b8be Compare August 25, 2026 22:47
@robobun robobun changed the title install(security-scanner): gate post-start() deref on Ok to avoid over-deref SSLConfig: fix stale allocator names in dupe_z doc comments Aug 25, 2026
@robobun

robobun commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main (1f9b8bea7d on top of 82123d3a61). The conflict was in security_scanner.rs, and I resolved it by dropping that hunk.

#40478 already fixed the over-deref on main. finish_spawn now returns before the deref when start() fails (src/install/PackageManager/security_scanner.rs:1339), and writer_local and the guard's w are RefPtrs that release on Drop. So the Err path ends at refcount 0 through the guard, which is what this PR set out to do.

What remains is the SSLConfig.rs doc comment fix from #30875 (comment). The two comments still said mi_free and "mimalloc" on main. I updated the title, the body, and the commit message to match. cargo check -p bun_runtime is clean and bun bd builds.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — comment-only fix aligning two stale doc lines with the block comment above them.

Checked that bun_core::dupe_z allocates via bun_alloc::default_alloc::malloc (util.rs:3685) and its own doc says it pairs with free_sensitive, so both wording changes are accurate.
Confirmed the diff touches only the two /// lines; no code, no behavior change.
CI failures (node-http-backpressure SIGKILL, WebSocket close timeout) are unrelated to a comment-only change.

Extended reasoning...

Overview

This PR edits two one-line doc comments on the zbox_into_raw and dupe_z helper functions in src/runtime/socket/SSLConfig.rs. It replaces mi_free with free_sensitive and "mimalloc allocation" with "default-allocator allocation". No executable code is touched. The security_scanner.rs refcount fix that this PR originally carried was superseded by #40478 and dropped on rebase; the remaining diff is exactly two comment lines.

Security risks

None. The change is documentation-only and does not alter allocation, freeing, or any control flow. The comments describe an existing allocator contract; the wording is being corrected to match what the code already does.

Level of scrutiny

Very low. I verified the factual claims directly: bun_core::dupe_z (src/bun_core/util.rs:3684) allocates with bun_alloc::default_alloc::malloc and its doc explicitly says it "Pairs with [free_sensitive]"; free_sensitive_cstr (src/bun_alloc/lib.rs:1027) frees via default_alloc::free. Under cfg(bun_asan) the default allocator is libc rather than mimalloc, so the old "mimalloc" / "mi_free" wording was indeed stale. The block comment immediately above these two lines (lines 52–59) already states the correct contract; this PR just brings the two per-function doc comments into agreement with it.

Other factors

No test is expected for a comment-only change. The CI failures on the older build are known flakes in unrelated HTTP tests and cannot be caused by comment edits. There are no prior reviews from me on this PR and no outstanding reviewer requests on the current revision.

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Closing: comment-only change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants